package com.bocai.user.service;

import com.alibaba.fastjson.TypeReference;
import com.bocai.CacheKey;
import com.bocai.Constants;
import com.bocai.PageResult;
import com.bocai.Response;
import com.bocai.aspect.LoginChecker;
import com.bocai.config.RedisAdapter;
import com.bocai.duobao.model.enums.SpendType;
import com.bocai.duobao.model.enums.WinStatus;
import com.bocai.duobao.service.DuobaoService;
import com.bocai.user.mapper.UserMapper;
import com.bocai.user.model.*;
import com.bocai.user.model.entity.CodepayRecord;
import com.bocai.user.model.vo.AuthInfo;
import com.bocai.user.model.vo.SpendRecord;
import com.bocai.user.model.vo.UserAccountInfo;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author create by VIC.Huang
 * @date 2018/1/22
 */
@Service
public class UserService {

    @Autowired
    private RedisAdapter redisAdapter;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private DuobaoService duobaoService;

    public User getUserByOpenId(String openid) {
        return userMapper.selectUserByOpenId(openid);
    }

    public User getUserById(Long userId) {
        return userMapper.selectUserById(userId);
    }

    /**
     * 保存用户信息/token到缓存中
     *
     * @param user
     * @return
     */
    public AuthInfo saveLoginInfo(User user) {
        String token = RandomStringUtils.randomAlphabetic(10) + user.getId() + DigestUtils.sha512Hex(System.currentTimeMillis() + "");
        AuthInfo authInfo = new AuthInfo();
        authInfo.setUserId(user.getId()).setHeadImgUrl(user.getWxHeadImg())
                .setNickname(user.getNickName()).setToken(token)
                .setBalance(getUserAsset(user.getId()).getAsset());
        redisAdapter.set(CacheKey.USER_TOKEN + user.getId(), authInfo.getToken());
        redisAdapter.setex(CacheKey.TOKEN_PREFIX + token, Constants.TOKEN_EXPIRE, authInfo);
        return authInfo;
    }

    @Transactional
    public boolean registerWxUser(User user) {
        if (userMapper.insertWxUser(user) > 0) {
            if (userMapper.initUserAsset(user.getId()) > 0) {
                return true;
            }
        }
        return false;
    }

    public UserAsset getUserAsset(Long userId) {
        return userMapper.selectUserAsset(userId);
    }

    public boolean checkEnoughBalance(Long userId, BigDecimal totalBetPrice) {
        UserAsset asset = userMapper.selectUserAsset(userId);
        // totalBetPrice > asset
        if (totalBetPrice.compareTo(asset.getAsset()) == 1) {
            return false;
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean deductBalance(Long userId, BigDecimal money, String spendType) {
        if (checkEnoughBalance(userId, money)) {
            if (userMapper.updateUserAsset(userId, money.negate(), BigDecimal.ZERO) > 0) {
                userMapper.insertSpendRecord(userId, money.negate(), spendType);

                // 更新用户信息-余额缓存
                String userToken = redisAdapter.get(CacheKey.USER_TOKEN + userId);
                AuthInfo authInfo = redisAdapter.get(CacheKey.TOKEN_PREFIX + userToken, new TypeReference<AuthInfo>(){});
                authInfo.setBalance(authInfo.getBalance().subtract(money));
                redisAdapter.setex(CacheKey.TOKEN_PREFIX + userToken, Constants.TOKEN_EXPIRE, authInfo);

                return true;
            }
        }
        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean increaseBalance(Long userId, BigDecimal money, String spendType) {
        if (userMapper.updateUserAsset(userId, money, BigDecimal.ZERO) > 0) {
            userMapper.insertSpendRecord(userId, money, spendType);

            // 更新用户信息-余额缓存
            String userToken = redisAdapter.get(CacheKey.USER_TOKEN + userId);
            AuthInfo authInfo = redisAdapter.get(CacheKey.TOKEN_PREFIX + userToken, new TypeReference<AuthInfo>(){});
            authInfo.setBalance(authInfo.getBalance().add(money));
            redisAdapter.setex(CacheKey.TOKEN_PREFIX + userToken, Constants.TOKEN_EXPIRE, authInfo);

            return true;
        }
        return false;
    }

    private static final int PAGE_SIZE = 10;

    public PageResult<BetRecord> getBetRecords(Long userId, Integer type, Integer page) {
        String winStatus = null;
        if (2 == type) {
            winStatus = "WIN";
        } else if (3 == type) {
            winStatus = "LOSE";
        }

        List<BetRecord> betRecords = userMapper.selectBetRecordsByUserId(userId, winStatus, (page - 1) * PAGE_SIZE, PAGE_SIZE);
        // 处理betNum winResult 与 totalBetPrice
        for (BetRecord betRecord : betRecords) {
            // betNum
            if (Constants.TWO.containsKey(betRecord.getBetNum())) {
                betRecord.setBetNum("尾数：" + Constants.TWO.get(betRecord.getBetNum()));
            } else if (Constants.FOUR.containsKey(betRecord.getBetNum())) {
                betRecord.setBetNum("后两位：" + Constants.FOUR.get(betRecord.getBetNum()));
            } else if (Constants.TEN.containsKey(betRecord.getBetNum())) {
                betRecord.setBetNum("尾数：" + Constants.TEN.get(betRecord.getBetNum()));
            }
            // winResult
            if (WinStatus.WIN.getCode().equals(betRecord.getWinStatus())) {
                betRecord.setWinResult(1000);
            } else if (WinStatus.LOSE.getCode().equals(betRecord.getWinStatus())) {
                betRecord.setWinResult(1001);
            } else {
                betRecord.setWinResult(duobaoService.getLeftSecond());
            }
            // totalBetPrice
            betRecord.setTotalBetPrice(betRecord.getPerPrice().multiply(new BigDecimal(betRecord.getBetAmount())));
            // openCode
            if (StringUtils.isBlank(betRecord.getOpenCode())) {
                betRecord.setOpenCode("待揭晓");
            }
        }

        return new PageResult<>(
                page,
                userMapper.countBetRecordsByUserId(userId, winStatus),
                betRecords);
    }

    public UserAccountInfo getUserAccountInfo(Long userId) {
        User user = userMapper.selectUserById(userId);
        return new UserAccountInfo()
                .setBalance(userMapper.selectUserAsset(userId).getAsset())
                .setTodayTake(userMapper.selectBetFrequency(userId, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), null))
                .setTodayLost(userMapper.selectBetFrequency(userId, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), WinStatus.LOSE.getCode()))
                .setTodayWin(userMapper.selectBetFrequency(userId, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), WinStatus.WIN.getCode()))
                .setTotalWin(userMapper.selectBetFrequency(userId, null, WinStatus.WIN.getCode()))
                .setQrcode(user != null ? user.getWithdrawCollectQrcode() : "");
    }

    public PageResult<SpendRecord> walletDetail(Long userId, Integer page) {
        return new PageResult<>(
            page,
            userMapper.countSpendRecord(userId, Arrays.asList(SpendType.BET.getCode(), SpendType.WIN.getCode(), SpendType.RECHARGE.getCode())),
            userMapper.selectSpendRecrod(userId, Arrays.asList(SpendType.BET.getCode(), SpendType.WIN.getCode(), SpendType.RECHARGE.getCode()), (page - 1) * Constants.PAGE_SIZE, Constants.PAGE_SIZE)
        );
    }

    public PageResult<SpendRecord> withdrawRecords(Long userId, Integer page) {
        return new PageResult<>(
                page,
                userMapper.countSpendRecord(userId, Arrays.asList(SpendType.WITHDRAW.getCode())),
                userMapper.selectSpendRecrod(userId, Arrays.asList(SpendType.WITHDRAW.getCode()), (page - 1) * Constants.PAGE_SIZE, Constants.PAGE_SIZE)
        );
    }

    @Transactional(rollbackFor = Exception.class)
    public void savePayRecord(CodepayRecord codepayRecord) {
        if(userMapper.countCodepayRecord(codepayRecord.getPay_no()) > 0) {
            return;
        }
        //-- 保存支付回调参数
        userMapper.insertCodepayRecord(codepayRecord);
        //-- 保存用户充值记录
        userMapper.insertSpendRecord(Long.parseLong(codepayRecord.getPay_id()), new BigDecimal(codepayRecord.getMoney()), SpendType.RECHARGE.getCode());
        //-- 更新用户资产值
        userMapper.updateUserAsset(Long.parseLong(codepayRecord.getPay_id()), new BigDecimal(codepayRecord.getMoney()), BigDecimal.ZERO);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean withdraw(String token, int money, String qrcode) {
        AuthInfo authInfo = redisAdapter.get(CacheKey.TOKEN_PREFIX + token, new TypeReference<AuthInfo>(){});
        if(null != authInfo && StringUtils.isNotBlank(qrcode)) {
            BigDecimal withdrawMoney = new BigDecimal(money);
            Long userId = authInfo.getUserId();
            UserAsset asset = userMapper.selectUserAsset(userId);
            if(asset.getAsset().compareTo(withdrawMoney) == -1) {
                throw new RuntimeException("超出账户可提现金额");
            }
            // 扣减账户余额
            userMapper.updateUserAsset(userId, withdrawMoney.negate(), withdrawMoney);
            // 插入用户收支记录
            userMapper.insertSpendRecord(userId, withdrawMoney.negate(), SpendType.WITHDRAW.getCode());
            // 插入用户提现记录
            userMapper.insertWithdrawRecord(userId, withdrawMoney, qrcode);
            // 更新提现码
            userMapper.updateWithdrawCollectQrcode(userId, qrcode);

            return true;
        }else {
            throw new RuntimeException("无法获取提现用户信息");
        }
    }

    public boolean updateWxUser(User userModel) {
        return userMapper.updateWxUser(userModel) > 0;
    }

    public Map<String, BigDecimal> commissionProfit(String token) {
        AuthInfo authInfo = redisAdapter.get(CacheKey.TOKEN_PREFIX + token, new TypeReference<AuthInfo>(){});
        return new HashMap<String, BigDecimal>(2) {{
            put("total", userMapper.sumCommissionAndPayback(authInfo.getUserId(), null));
            put("today", userMapper.sumCommissionAndPayback(authInfo.getUserId(), LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
        }};
    }

    public PageResult<CommissionRecord> commissionRecords(String token, Integer page, Integer pageSize) {
        AuthInfo authInfo = redisAdapter.get(CacheKey.TOKEN_PREFIX + token, new TypeReference<AuthInfo>(){});
        return new PageResult<>(page, userMapper.countCommissionRecords(authInfo.getUserId()),
                userMapper.selectCommissionRecords(authInfo.getUserId(), (page-1)*pageSize, pageSize));
    }

    public List<CommissionPlayer> commissionPlayerList(String token) {
        AuthInfo authInfo = redisAdapter.get(CacheKey.TOKEN_PREFIX + token, new TypeReference<AuthInfo>(){});
        return userMapper.selectCommissionPlayer(authInfo.getUserId());
    }

    public User getUserByName(String username) {
        return userMapper.selectUserByName(username);
    }

    public boolean registerUser(User user) {
        if (userMapper.insertUser(user) > 0) {
            if (userMapper.initUserAsset(user.getId()) > 0) {
                return true;
            }
        }
        return false;
    }

    public PageResult<PaybackRecord> paybackRecords(String token, Integer page, Integer pageSize) {
        AuthInfo authInfo = redisAdapter.get(CacheKey.TOKEN_PREFIX + token, new TypeReference<AuthInfo>(){});
        return new PageResult<>(page, userMapper.countPaybackRecords(authInfo.getUserId()),
                userMapper.selectPaybackRecords(authInfo.getUserId(), (page-1)*pageSize, pageSize));
    }
}
